home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 October: Mac OS SDK / Dev.CD Oct 00 SDK1.toast / Development Kits / Mac OS / Multiprocessing 2.1v2 SDK / Sample Code / HappyTrails ƒ / HappyTrails.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-05-04  |  17.9 KB  |  793 lines  |  [TEXT/CWIE]

  1. /**\
  2. |**|    HappyTrails.c
  3. \**/
  4.  
  5. #include <stdio.h>
  6. #include <stdarg.h>
  7.  
  8. #include "HappyTrails.h"
  9.  
  10. // typdef's, struct's, define's, enum's, etc.
  11.  
  12. #define kMPStackSize 0        // use default stack size
  13. #define kMPTaskOptions 0    // use no options
  14.  
  15. enum
  16. {
  17.     mAppleMenu = 128,
  18.         iAboutBox = 1,
  19.     mFileMenu = 129,
  20.         iQuit = 1,
  21.     mTasksMenu = 130,
  22.     mWeightMenu = 131
  23. };
  24.  
  25. typedef struct BallStruct {
  26.     Point        fPosition;
  27.     Point        fDelta;
  28.     RGBColor    fColor;
  29.     UInt32        fLength;
  30. } BallRec,*BallPtr,**BallHdl;
  31.  
  32. typedef struct MyTaskStruct {
  33.     MPTaskID        fTaskID;
  34.     MPTaskWeight    fWeight;
  35.     MPSemaphoreID    fSemaphoreID;
  36.     Rect            fBounds;
  37.     UInt32            fIndex;
  38.     UInt32            fBallNum;
  39.     BallPtr         fBallPtr;
  40. }MyTaskRec,*MyTaskPtr,**MyTaskHdl;
  41.  
  42. // external globals
  43.  
  44. UInt32        gNumTasks = 0;
  45.  
  46. // local (static) globals
  47.  
  48. static PixMapHandle     gWindowPixMap = nil;
  49. static UInt8*             gWindowBasePtr = nil;
  50.  
  51. static GWorldPtr        gOffscreenPtr = nil;
  52. static Rect                gOffscreenRect = {0,0,kBallHeight,kBallWidth};
  53. static PixMapHandle     gOffscreenPixMap = nil;
  54. static UInt8*             gOffscreenBasePtr = nil;
  55.  
  56. static Boolean             gPaused = false;
  57. static Boolean             gLoop = true;
  58.  
  59. static MyTaskPtr         gMyTaskPtrs = nil;
  60. static MPQueueID         gNotificationQueueID;
  61.  
  62. static MenuHandle         gContextualMenuHdl = nil;
  63.  
  64. // local function prototypes
  65.  
  66. static OSStatus Ball_Task(void* pMyTaskPtr);
  67.  
  68. static OSStatus New_Ball(MyTaskPtr pMyTaskPtr);
  69. static void Draw_Ball(const Rect* pRectPtr,const RGBColor* pBallColorPtr);
  70. static void Blit_Ball(const Rect *pBallRect,const RGBColor* pBallColorPtr);
  71.  
  72. static Boolean rects_Intersect(const Rect* pRect1,const Rect* pRect2);
  73. static void offset_rect (Rect *r,const short dh,const short dv);
  74. static SInt16 random();
  75.  
  76. #define ABS(x) ((x) < 0 ? -(x) : (x))
  77.  
  78. // local functions
  79.  
  80.  
  81. /**\
  82. |**|    MPGetTaskWeight
  83. \**/
  84.  
  85. static pascal OSStatus MPGetTaskWeight(MPTaskID task,MPTaskWeight* weight)
  86. {
  87.     MPTaskInfo tMPTaskInfo;
  88.     OSStatus error;
  89.  
  90.     if (!weight) return paramErr;
  91.  
  92.     tMPTaskInfo.version = kMPTaskInfoVersion;
  93.  
  94.     error = MPExtractTaskState(task,kMPTaskStateTaskInfo,(void*) &tMPTaskInfo);
  95.  
  96.     if (error == noErr)
  97.         *weight = tMPTaskInfo.weight;
  98.  
  99.     return error;
  100. }
  101.  
  102. /**\
  103. |**|    printf to debugger
  104. \**/
  105.  
  106. static int dprintf(const char *format,...)
  107. {
  108.     char buffer[257]; /* [length byte] + [255 string bytes] + [null] */
  109.     va_list arglist;
  110.     int return_value = 0;
  111.     
  112.     va_start(arglist, format);
  113.     return_value = vsprintf(buffer, format, arglist);
  114.     va_end(arglist);
  115.  
  116. #if TARGET_API_MAC_CARBON
  117.     CopyCStringToPascal(buffer,(unsigned char*) buffer);
  118.     DebugStr((unsigned char*) buffer);    
  119. #else
  120.     debugstr(buffer);    
  121. #endif TARGET_API_MAC_CARBON
  122.  
  123.     return return_value;
  124. }
  125.  
  126.  
  127. /**\
  128. |**|    convert index (1 <-> gNumTasks) to weight
  129. \**/
  130.  
  131. static MPTaskWeight TaskToWeight(UInt32 pTaskIndex)
  132. {
  133.     float_t e = exp(1); // 2.718281828;
  134.     if (gNumTasks <= 1)
  135.         return 100;
  136.     else
  137.         return (50 * (pow(e,(log(200) * pTaskIndex) / (gNumTasks - 1))));
  138. }
  139.  
  140. /**\
  141. |**|    Initialize everything for HappyTrails, make sure we can run
  142. \**/
  143.  
  144. OSStatus HT_Init(UInt32 pNumTasks)
  145. {
  146.     OSStatus    error;
  147.     UInt32        index;
  148.  
  149.     if (!MPLibraryIsLoaded())
  150.         return kMPInsufficientResourcesErr;
  151.  
  152.     gLoop = true;
  153.     gNumTasks = pNumTasks;
  154. #if TARGET_API_MAC_CARBON
  155.  
  156.     gWindowPixMap = GetPortPixMap(GetWindowPort(gWindowPtr));
  157. #else
  158.     gWindowPixMap = ((CWindowPtr) gWindowPtr)->portPixMap;
  159. #endif TARGET_API_MAC_CARBON
  160.  
  161.     if (!LockPixels(gWindowPixMap))
  162.         DebugStr("\p|HT_Init-F-LockPixels(gWindowPixMap) error.;es");
  163.  
  164.     gWindowBasePtr = (UInt8*) GetPixBaseAddr(gWindowPixMap);
  165.  
  166.     {
  167.         RGBColor whiteRGBColor = {-1,-1,-1};
  168.         CGrafPtr savePort;
  169.         GDHandle saveGDHdl;
  170.  
  171.         GetGWorld(&savePort, &saveGDHdl);
  172.  
  173.         // Create an offscreen GWorld and draw our SillyBall into it.
  174.         // The MP tasks will copy from this GWorlds PixMap to our
  175.         // windows PixMap changing the color on-the-fly.
  176.  
  177.         error = NewGWorld(&gOffscreenPtr, 1, &gOffscreenRect, nil, nil, keepLocal);
  178.         if (error != noErr)
  179.             dprintf("|HT_Init-F-NewGWorld error: %d.;es",error);
  180.  
  181.         gOffscreenPixMap = GetGWorldPixMap(gOffscreenPtr);
  182.         if (!LockPixels(gOffscreenPixMap))
  183.             DebugStr("\p|HT_Init-F-LockPixels(gOffscreenPixMap) error.;es");
  184.  
  185.         gOffscreenBasePtr = (UInt8*) GetPixBaseAddr(gOffscreenPixMap);
  186.  
  187.         SetGWorld(gOffscreenPtr, nil);
  188.         EraseRect(&gOffscreenRect);
  189.         Draw_Ball(&gOffscreenRect,&whiteRGBColor);
  190.  
  191.         SetGWorld(savePort, saveGDHdl);
  192.     }
  193.  
  194.     // Initialize remaining globals
  195.     gNotificationQueueID = NULL;
  196.  
  197.     // create the notification queue            
  198.     error = MPCreateQueue(&gNotificationQueueID);
  199.     if (error != noErr)
  200.     {
  201.         dprintf("|HT_Init-F-MPCreateQueue(&gNotificationQueueID) error: %d.;g",error);
  202.         return error;
  203.     }
  204.  
  205.     // allocate the task records
  206.     gMyTaskPtrs = (MyTaskPtr) NewPtrClear(sizeof(MyTaskRec) * gNumTasks);
  207.     if (!gMyTaskPtrs)
  208.     {
  209.         error = MemError();
  210.         if (error == noErr)
  211.             error = memFullErr;
  212.         return error;
  213.     }
  214.  
  215.     {
  216.         UInt16 width = gWindowRect.right - gWindowRect.left;
  217.         UInt16 height = gWindowRect.bottom - gWindowRect.top;
  218.         UInt16 rows,cols;
  219.  
  220.         cols = sqrt(gNumTasks * 4 / 3);
  221.         rows = gNumTasks / cols;
  222.  
  223.         if ((rows * cols) < gNumTasks)
  224.             rows++;
  225.  
  226.         width /= cols;
  227.         height /= rows;
  228.  
  229.         EraseRect(&gWindowRect);
  230.  
  231.         for (index = 0; (index < gNumTasks) && (error == noErr); index++)
  232.         {
  233.             MyTaskPtr tMyTaskPtr = &gMyTaskPtrs[index];
  234.             Rect tRect;
  235.  
  236.             tRect.bottom = (tRect.top = height * (index / cols)) + height;
  237.             tRect.right = (tRect.left = width * (index % cols)) + width;
  238.  
  239.             FrameRect(&tRect);
  240.  
  241.             tMyTaskPtr->fBounds = tRect;
  242.             tMyTaskPtr->fWeight = TaskToWeight(index); // 16 * (1 << index);
  243.             tMyTaskPtr->fIndex = index;
  244.  
  245.             tMyTaskPtr->fBallPtr = (BallPtr) NewPtrClear(sizeof(BallRec) * kNumBalls);
  246.  
  247. #    if !SMP_TEST
  248.             error = MPCreateTask(Ball_Task, tMyTaskPtr,    // task entry point & parameter
  249.                 kMPStackSize, gNotificationQueueID,        // stack size & notification queue ID
  250.                 tMyTaskPtr, NULL, kMPTaskOptions,        // Notify params 1 & 2 and task options
  251.                 &tMyTaskPtr->fTaskID);                    // task id
  252.             if (error != noErr)
  253.             {
  254.                 dprintf("|HT_Init-F-MPCreateTask error: %d.;g",error);
  255.                 return error;
  256.             }
  257.  
  258.             error = MPSetTaskWeight(tMyTaskPtr->fTaskID,tMyTaskPtr->fWeight);
  259.             if (error != noErr)
  260.             {
  261.                 dprintf("|HT_Init-F-MPSetTaskWeight error: %d.;g",error);
  262.                 return error;
  263.             }
  264.  
  265.  
  266.             // create the ready Semaphore
  267.             error = MPCreateSemaphore(1,1,&tMyTaskPtr->fSemaphoreID);
  268.             if (error != noErr)
  269.             {
  270.                 dprintf("|HT_Init-F-MPCreateSemaphore error: %d.;g",error);
  271.                 return error;
  272.             }
  273.  
  274. #    endif !SMP_TEST
  275.         }
  276.     }
  277.  
  278.     gContextualMenuHdl = GetMenu(mWeightMenu);
  279.     InsertMenu(gContextualMenuHdl, -1);
  280.  
  281.     // If something went wrong, just go back to single processor mode
  282.     if (error != noErr)
  283.         gNumTasks = 1;
  284.  
  285.     return error;
  286. }    // HT_Init
  287.  
  288. /**\
  289. |**|    Cleanup everything
  290. \**/
  291.  
  292. void HT_Term(void)
  293. {
  294.     OSStatus error;
  295.     UInt32    index;
  296.  
  297.     gInBackGround = gLoop = false;
  298.  
  299.     if (gMyTaskPtrs)
  300.     {
  301.         for (index = 0; index < gNumTasks; index++)
  302.         {
  303.             MyTaskPtr tMyTaskPtr = &gMyTaskPtrs[index];
  304.             if (tMyTaskPtr->fTaskID != NULL)
  305.             {
  306.                 // terminate this task
  307.                 error = MPTerminateTask(tMyTaskPtr->fTaskID, kMPTaskAbortedErr);
  308.                 if (error != noErr)
  309.                     dprintf("|HT_Term-F-MPTerminateTask error: %d.;g",error);
  310.  
  311.                 // Now wait for notification that this task has terminated
  312.                 while ((error = MPWaitOnQueue(gNotificationQueueID,
  313.                     (void*) &tMyTaskPtr, NULL, NULL, kDurationImmediate)) == noErr)
  314.                 {
  315.                     if (tMyTaskPtr->fTaskID != NULL)
  316.                     {
  317.                         tMyTaskPtr->fTaskID = NULL;
  318.  
  319.                         if (tMyTaskPtr->fBallPtr)
  320.                             DisposePtr((Ptr) tMyTaskPtr->fBallPtr);
  321.                         tMyTaskPtr->fBallPtr = nil;
  322.                     }
  323.                 }
  324.  
  325.                 // delete the ready Semaphore
  326.                 if (tMyTaskPtr->fSemaphoreID != NULL)
  327.                 {
  328.                     MPDeleteSemaphore(tMyTaskPtr->fSemaphoreID);
  329.                     tMyTaskPtr->fSemaphoreID = NULL;
  330.                 }
  331.             }
  332.         }
  333.  
  334.         // delete the notification queue
  335.         if (gNotificationQueueID != NULL)
  336.         {
  337.             MPDeleteQueue(gNotificationQueueID);
  338.             gNotificationQueueID = NULL;
  339.         }
  340.  
  341.         // delete the task data
  342.         DisposePtr((Ptr)gMyTaskPtrs);
  343.         gMyTaskPtrs = NULL;
  344.     }
  345.  
  346.     if (gOffscreenPtr)
  347.         DisposeGWorld(gOffscreenPtr);
  348.     gOffscreenPtr = nil;
  349. }
  350.  
  351. /**\
  352. |**|    Handle a null event
  353. \**/
  354.  
  355. void HT_DoNull(void)
  356. {
  357.     static Boolean sInBackGround = false;
  358. # if SMP_TEST
  359.     UInt32 index;
  360.  
  361.     gLoop = false;
  362.     for (index = 0; index < gNumTasks;index++)
  363.         Ball_Task((void*) &gMyTaskPtrs[index]);
  364. # else
  365. //    MPYield();
  366. # endif SMP_TEST
  367.  
  368.     if (sInBackGround != gInBackGround)
  369.     {
  370.         sInBackGround = gInBackGround;
  371.         if (!sInBackGround)
  372.         {
  373.             SInt32 index;
  374.             for (index = 0; index < gNumTasks;index++)
  375.                 MPSignalSemaphore(gMyTaskPtrs[index].fSemaphoreID);
  376.         }
  377.     }
  378. }
  379.  
  380. /**\
  381. |**|    Handle a mouse down event
  382. \**/
  383.  
  384. void HT_DoClick(const EventRecord *pEventPtr)
  385. {
  386.     MyTaskPtr tMyTaskPtr = nil;
  387.     Point where = pEventPtr->where;
  388.     UInt32 index;
  389.  
  390.     GlobalToLocal(&where);
  391.  
  392.     for (index = 0;index < gNumTasks;index++)
  393.     {
  394.         if (PtInRect(where,&gMyTaskPtrs[index].fBounds))
  395.             tMyTaskPtr = &gMyTaskPtrs[index];
  396.     }
  397.  
  398.     if (!tMyTaskPtr || !gContextualMenuHdl)
  399.         goto beep;
  400.  
  401.     {
  402.         SInt32 weight = (SInt32) tMyTaskPtr->fWeight;
  403.         SInt16 count = CountMenuItems(gContextualMenuHdl);
  404.         Boolean first = true;
  405. #if 1
  406.         SInt32 value;
  407.  
  408.         for (index = 1;index <= count;index++)
  409.         {
  410.             Str255 tStr255;
  411.             UInt32 temp = gNumTasks;
  412.  
  413.             gNumTasks = count;
  414.             value = TaskToWeight(index - 1);
  415.             gNumTasks = temp;
  416.  
  417.             NumToString(value, tStr255);
  418.             SetMenuItemText(gContextualMenuHdl, index, tStr255);
  419. #if CALL_NOT_IN_CARBON
  420.             EnableItem(gContextualMenuHdl, index);
  421. #else
  422.             EnableMenuItem(gContextualMenuHdl, index);
  423. #endif CALL_NOT_IN_CARBON
  424.  
  425.             if (value < weight)
  426.                 CheckMenuItem(gContextualMenuHdl, index, false);
  427.             else
  428.             {
  429.                 CheckMenuItem(gContextualMenuHdl, index, first);
  430.                 if (first)
  431.                     weight = value;
  432.                 first = false;
  433.             }
  434.         }
  435.  
  436.         if (IsShowContextualMenuClick(pEventPtr))
  437.         {
  438.             UInt32 outUserSelectionType;
  439.             SInt16 outMenuID;
  440.             UInt16 outMenuItem;
  441.             OSStatus tOSStatus;
  442.  
  443.             gPaused = true;
  444.             tOSStatus = ContextualMenuSelect(gContextualMenuHdl,
  445.                                             pEventPtr->where,
  446.                                             false,
  447.                                             nil,
  448.                                             nil,
  449.                                             nil,
  450.                                             &outUserSelectionType,
  451.                                             &outMenuID,
  452.                                             &outMenuItem);
  453.             gPaused = false;
  454.             if ((tOSStatus == noErr) && (outUserSelectionType == kCMMenuItemSelected))
  455.             {
  456.                 if (outMenuID == mWeightMenu)
  457.                 {
  458.                     Str255 tStr255;
  459.                     OSStatus error;
  460.  
  461.                     GetMenuItemText(gContextualMenuHdl, outMenuItem, tStr255);
  462.                     StringToNum(tStr255, &value);
  463.                     tMyTaskPtr->fWeight = value;
  464.  
  465.                     index = (long) tMyTaskPtr->fIndex;
  466.  
  467.                     error = MPSetTaskWeight(tMyTaskPtr->fTaskID, value);
  468.                     if (error != noErr)
  469.                         dprintf("|HT_DoClick-F-MPSetTaskWeight error: %d.;g",error);
  470.                 }
  471.             }
  472.             return;
  473.         }
  474.         else
  475. #endif
  476.         {
  477.             if (pEventPtr->modifiers & optionKey)
  478.                 EraseRect(&gWindowRect);
  479.             else
  480.                 EraseRect(&tMyTaskPtr->fBounds);
  481.             return;
  482. //            goto beep;
  483.         }
  484.     }
  485. beep:
  486.         SysBeep(15);
  487. }
  488.  
  489. /**\
  490. |**|    This is the MP task entrypoint
  491. \**/
  492.  
  493. OSStatus Ball_Task(void* pMyTaskPtr)
  494. {
  495.     MyTaskPtr tMyTaskPtr = (MyTaskPtr) pMyTaskPtr;
  496.  
  497.     // wait for the ready Semaphore
  498.     MPWaitOnSemaphore(tMyTaskPtr->fSemaphoreID,kDurationForever);
  499.  
  500.     do {
  501.         New_Ball(tMyTaskPtr);
  502.         while (gInBackGround)
  503.         {
  504.             // wait for the ready Semaphore
  505.             MPWaitOnSemaphore(tMyTaskPtr->fSemaphoreID,kDurationForever);
  506.         }
  507.     } while (gLoop);
  508.     return noErr;
  509. }
  510.  
  511. /**\
  512. |**|    Draw the next ball
  513. \**/
  514.  
  515. static OSStatus New_Ball(MyTaskPtr pMyTaskPtr)
  516. {
  517.     Rect        ballRect;
  518.     Rect        bounds = pMyTaskPtr->fBounds;
  519.     UInt32        newLeft,newTop;
  520.     UInt32        width = bounds.right - bounds.left;
  521.     UInt32        height = bounds.bottom - bounds.top;
  522.     RGBColor    ballColor;
  523.  
  524.     BallPtr        tBallPtr;
  525.  
  526.     if (pMyTaskPtr->fBallNum >= kNumBalls)
  527.         pMyTaskPtr->fBallNum = 0;
  528.  
  529.     if (gPaused)
  530.         return noErr;
  531.  
  532.     tBallPtr = &pMyTaskPtr->fBallPtr[pMyTaskPtr->fBallNum++];
  533.  
  534.     if (tBallPtr->fLength)
  535.     {
  536.         newTop = tBallPtr->fPosition.v;
  537.         newLeft = tBallPtr->fPosition.h;
  538.  
  539.         ballRect.bottom = (ballRect.top = newTop) + kBallHeight;
  540.         ballRect.right = (ballRect.left = newLeft) + kBallWidth;
  541.  
  542.         ballColor = tBallPtr->fColor;
  543.         if (tBallPtr->fLength == 1)
  544.         {
  545.             ballColor.red ^= 0x8000;
  546.             ballColor.green ^= 0x8000;
  547.             ballColor.blue ^= 0x8000;
  548.         }
  549.     }
  550.     else
  551.     {
  552.         // 
  553.         //    Make a random new color for the ball.
  554.         //
  555.         ballColor.red   = (unsigned short) random();
  556.         ballColor.green = (unsigned short) random();
  557.         ballColor.blue  = (unsigned short) random();
  558.         tBallPtr->fColor = ballColor;
  559.  
  560.         //    
  561.         //    Make a random new location for the ball, that is normalized to the window size.  
  562.         //    This makes the Integer from random into a number that is 0..bounds.bottom
  563.         //    and 0..bounds.right.  They are normalized so that we don't spend most of our
  564.         //    time drawing in places outside of the window.
  565.         //
  566.         do
  567.         {
  568.             newTop = 32767 + random();    newLeft = 32767 + random();
  569.             newTop = bounds.top + ((newTop * (height - kBallHeight)) / 65536);
  570.             newLeft = bounds.left + ((newLeft * (width - kBallWidth)) / 65536);
  571.  
  572.             ballRect.bottom = (ballRect.top = newTop) + kBallHeight;
  573.             ballRect.right = (ballRect.left = newLeft) + kBallWidth;
  574.  
  575.         } while (    (ballRect.top < bounds.top) ||
  576.                     (ballRect.left < bounds.left) ||
  577.                     (ballRect.bottom >= bounds.bottom) ||
  578.                     (ballRect.right >= bounds.right)
  579.                 );
  580.  
  581.         // 
  582.         //    Make a random new delta direction
  583.         //
  584.  
  585.         do {
  586.             tBallPtr->fDelta.h = random() % 5;
  587.             tBallPtr->fDelta.v = random() % 5;
  588.         } while ((tBallPtr->fDelta.h == 0) || (tBallPtr->fDelta.v == 0));
  589.  
  590.         tBallPtr->fLength = 0x3F & random();    // width + ABS(random() % height);
  591.     }
  592.  
  593.     if (!rects_Intersect(&ballRect,&gMenuRect))
  594.         Blit_Ball(&ballRect,&ballColor);
  595.  
  596.     if (tBallPtr->fLength)
  597.         tBallPtr->fLength--;
  598.  
  599.     offset_rect(&ballRect,tBallPtr->fDelta.h,0);
  600.     if (ballRect.left <= bounds.left)
  601.     {
  602.         offset_rect(&ballRect, bounds.left - ballRect.left,0);
  603.         tBallPtr->fDelta.h = ABS(tBallPtr->fDelta.h);
  604.         do {tBallPtr->fDelta.v = (tBallPtr->fDelta.v + (random() % 2)) % 5;} while (!tBallPtr->fDelta.v);
  605.     }
  606.     else if (ballRect.right >= bounds.right)
  607.     {
  608.         offset_rect(&ballRect, bounds.right - ballRect.right,0);
  609.         tBallPtr->fDelta.h = -ABS(tBallPtr->fDelta.h);
  610.         do {tBallPtr->fDelta.v = (tBallPtr->fDelta.v + (random() % 2)) % 5;} while (!tBallPtr->fDelta.v);
  611.     }
  612.  
  613.     offset_rect(&ballRect,0,tBallPtr->fDelta.v);
  614.     if (ballRect.top <= bounds.top)
  615.     {
  616.         offset_rect(&ballRect,0, bounds.top - ballRect.top);
  617.         tBallPtr->fDelta.v = ABS(tBallPtr->fDelta.v);
  618.         do {tBallPtr->fDelta.h = (tBallPtr->fDelta.h + (random() % 2)) % 5;} while (!tBallPtr->fDelta.h);
  619.     }
  620.     else if (ballRect.bottom >= bounds.bottom)
  621.     {
  622.         offset_rect(&ballRect,0, bounds.bottom - ballRect.bottom);
  623.         tBallPtr->fDelta.v = -ABS(tBallPtr->fDelta.v);
  624.         do {tBallPtr->fDelta.h = (tBallPtr->fDelta.h + (random() % 2)) % 5;} while (!tBallPtr->fDelta.h);
  625.     }
  626.     tBallPtr->fPosition = *(Point*) &ballRect;
  627.  
  628.     return noErr;
  629. }
  630.  
  631. /**\
  632. |**|    draw a ball
  633. \**/
  634.  
  635. static void Draw_Ball(const Rect* pRectPtr,const RGBColor* pBallColorPtr)
  636. {
  637.     RGBColor invertRGBColor = *pBallColorPtr;
  638.     // 
  639.     //    Set that color as the new color to use in drawing.
  640.     //
  641.     RGBForeColor(pBallColorPtr);
  642.  
  643.     //
  644.     //    Move pen to the new location, and paint the colored ball.
  645.     //
  646.     PaintOval (pRectPtr);
  647.     
  648.     //
  649.     //    Move the pen to the middle of the new ball position, for the text
  650.     //
  651.     MoveTo((short)(pRectPtr->left + kBallWidth/2 - kTextSize), 
  652.         (short)(pRectPtr->top + kBallHeight/2 + kTextSize/2 -1));
  653.     
  654.     //    
  655.     //    Invert the color and draw the text there.  This won’t look quite right in 1 bit
  656.     //    mode, since the foreground and background colors will be the same.
  657.     //    Color QuickDraw special cases this to not invert the color, to avoid
  658.     //    invisible drawing.
  659.     //
  660.     InvertColor(&invertRGBColor); 
  661.     RGBForeColor(&invertRGBColor);
  662.  
  663.     // (
  664.     DrawString("\p;-)");
  665.  
  666.     InvertOval(&gOffscreenRect);
  667. }
  668.  
  669. /**\
  670. |**|    Blit a ball onscreen
  671. \**/
  672.  
  673. static void Blit_Ball(const Rect *pBallRect,const RGBColor* pBallColorPtr)
  674. {
  675.     UInt16 srcSkip = (*gOffscreenPixMap)->rowBytes & 0x3FFF;
  676.     UInt16 dstSkip = (*gWindowPixMap)->rowBytes & 0x3FFF;
  677.  
  678.     UInt8 *srcBase = gOffscreenBasePtr +
  679.         (srcSkip * (gOffscreenRect.top - (*gOffscreenPixMap)->bounds.top)) +
  680.         ((gOffscreenRect.left - (*gOffscreenPixMap)->bounds.left) >> 3);
  681.  
  682.     UInt8 *dstBase = gWindowBasePtr +
  683.         (dstSkip * (pBallRect->top - (*gWindowPixMap)->bounds.top));
  684.  
  685.     UInt16 r,c,pixelSize = (*gWindowPixMap)->pixelSize;
  686.  
  687.     UInt32 color32 = 0x00FFFFFF & (
  688.         (pBallColorPtr->red << 16) ^
  689.         (pBallColorPtr->green << 8) ^
  690.         (pBallColorPtr->blue << 0)
  691.     );
  692.     UInt16 color16 = 0x7FFF & color32;
  693.     UInt8 color8 = 0xFF & color16;
  694.  
  695.     switch (pixelSize)
  696.     {
  697.         case 1:        // b/w
  698.         case 2:        // 4 colors
  699.         case 4:        // 16 colors
  700.             break;    // Sorry, Homey don't play that! (Yet!)
  701.         case 8:        // 256 colors
  702.             dstBase += pBallRect->left - (*gWindowPixMap)->bounds.left;
  703.             break;
  704.         case 16:    // thousand colors
  705.             dstBase += (pBallRect->left - (*gWindowPixMap)->bounds.left) << 1;
  706.             break;
  707.         case 32:    // million colors
  708.             dstBase += (pBallRect->left - (*gWindowPixMap)->bounds.left) << 2;
  709.             break;
  710.     }
  711.  
  712.     for (r = gOffscreenRect.top;r < gOffscreenRect.bottom;r++)
  713.     {
  714.         UInt8 *srcPtr = srcBase,*dstPtr = dstBase;
  715.         UInt8 data = *srcPtr++;
  716.         for (c = gOffscreenRect.left;c < gOffscreenRect.right;c++)
  717.         {
  718.             UInt8 i = 7 - (c % 8);
  719.  
  720.             switch (pixelSize)
  721.             {
  722.                 case 1:        // b/w
  723.                 case 2:        // 4 colors
  724.                 case 4:        // 16 colors
  725.                     break;    // Sorry, Homey don't play that! (Yet!)
  726.                 case 8:        // 256 colors
  727.                     if ((data >> i) & 1)    // source bit is set
  728.                         *dstPtr = color8;
  729.                     dstPtr++;
  730.                     break;
  731.                 case 16:    // b/w depth
  732.                     if ((data >> i) & 1)    // source bit is set
  733.                         *(UInt16*)dstPtr = color16;
  734.                     dstPtr += 2;
  735.                     break;
  736.                 case 32:    // b/w depth
  737.                     if ((data >> i) & 1)    // source bit is set
  738.                         *(UInt32*)dstPtr = color32;
  739.                     dstPtr += 4;
  740.                     break;
  741.             }
  742.  
  743.             if (i == 0)
  744.                 data = *srcPtr++;
  745.         }
  746.         srcBase += srcSkip;
  747.         dstBase += dstSkip;
  748.     }
  749. }
  750.  
  751. /**\
  752. |**|    Test to see if rectangles intersect
  753. \**/
  754.  
  755. static Boolean rects_Intersect(const Rect* pRect1,const Rect* pRect2)
  756. {
  757.     if (pRect1->bottom <= pRect2->top)
  758.         return false;
  759.     if (pRect1->top >= pRect2->bottom)
  760.         return false;
  761.     if (pRect1->right <= pRect2->left)
  762.         return false;
  763.     if (pRect1->left >= pRect2->right)
  764.         return false;
  765.     return true;
  766. }
  767.  
  768. /**\
  769. |**|    move a rectangle
  770. \**/
  771.  
  772. static void offset_rect(Rect *r,const short dh,const short dv)
  773. {
  774.     r->top += dv;
  775.     r->left += dh;
  776.     r->bottom += dv;
  777.     r->right += dh;
  778. }
  779.  
  780. /**\
  781. |**|    generate a random number +/- 32768
  782. \**/
  783.  
  784. static SInt16 random()
  785. {
  786.     static SInt32 state = 314159;
  787.  
  788.     state = ((state * 1103515245) + 12345 ) & 0x7fffffff;
  789.  
  790.     return ((state >> 6) & 0xffff);
  791. }
  792.  
  793.